package com.leige.datasource.aspect;

import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.leige.datasource.annotation.DataSourceChange;
import com.leige.datasource.core.DynamicDataSource;

/**
 * 注解的方法，调用时会切换到指定的数据源
 *
 * @author tanghd
 */
@Aspect()
public class DataSourceAspect {
	private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

	@Pointcut(value = "@annotation(com.leige.datasource.annotation.DataSourceChange)")
	private void changeDataSource() {
	}

	/**
	 * 切面方法调用 时执行，调用顺序1
	 * 
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around(value = "changeDataSource()", argNames = "proceedingJoinPoint")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		MethodSignature ms = (MethodSignature) proceedingJoinPoint.getSignature();
		Method method = ms.getMethod();

		Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
		Method targetMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
		DataSourceChange annotation = targetMethod.getAnnotation(DataSourceChange.class);

		if (annotation == null) {
			annotation = method.getAnnotation(DataSourceChange.class);
			if (annotation == null)
				return proceedingJoinPoint.proceed();
		}
		try {
			if (annotation.slave()) {
				DynamicDataSource.useSlave();
				if(logger.isInfoEnabled())logger.debug("当前线程被切换到从库中去执行");
			} else {
				DynamicDataSource.useMaster();
				if(logger.isInfoEnabled())logger.debug("当前线程被切换到主库中去执行");
			}
			return proceedingJoinPoint.proceed();
		} finally {
			DynamicDataSource.reset();
		}
	}

	/**
	 * 切面方法进入后调用，调用顺序2
	 * 
	 * @param joinPoint
	 */
	@Before("changeDataSource()")
	public void before(JoinPoint joinPoint) {
	}

	/**
	 * 切面方法即将返回前调用，调用顺序3
	 * 
	 * @param jp
	 * @param error
	 */
	@After("changeDataSource()")
	public void after(JoinPoint joinPoint) {
	}

	/**
	 * 切面方法正常返回后调用，调用顺序4
	 * 
	 * @param joinPoint
	 * @param returnVal
	 */
	@AfterReturning(pointcut = "changeDataSource()", returning = "relust")
	public void afterReturning(JoinPoint joinPoint, Object relust) {
	}

	/**
	 * 切面方法异常返回后调用，调用顺序5
	 * 
	 * @param jp
	 * @param error
	 */
	@AfterThrowing(pointcut = "changeDataSource()", throwing = "error")
	public void afterThrowing(JoinPoint jp, Throwable error) {
	}

}
